package com.ut.richpath.util;

import com.ut.richpath.RichPath;
import com.ut.richpath.model.Group;
import com.ut.richpath.model.Vector;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.TextTool;
import ohos.app.Context;
import ohos.global.resource.Resource;
import ohos.global.resource.solidxml.Attribute;
import ohos.global.resource.solidxml.Node;
import ohos.global.resource.solidxml.SolidXml;
import ohos.javax.xml.parsers.SAXParser;
import ohos.javax.xml.parsers.SAXParserFactory;
import ohos.org.xml.sax.Attributes;
import ohos.org.xml.sax.InputSource;
import ohos.org.xml.sax.SAXException;
import ohos.org.xml.sax.XMLReader;
import ohos.org.xml.sax.helpers.DefaultHandler;

import java.util.List;
import java.util.Stack;

public class XmlParser {

    private static final String NAMESPACE = "http://schemas.huawei.com/res/ohos";

    public static void parseVector(Vector vector, int resId, Context context) throws Exception{
        SolidXml solidXml = context.getResourceManager().getSolidXml(resId);
        parseXML(context, vector, solidXml);
    }

    public static void parseVector(Vector vector, String rawFilePath, Context context) throws Exception{
        SAXParserFactory parserFactory = SAXParserFactory.newInstance();
        parserFactory.setNamespaceAware(true);
        SAXParser saxParser = parserFactory.newSAXParser();
        Resource resource = context.getResourceManager().getRawFileEntry(rawFilePath).openRawFile();//"entry/resources/rawfile/ic_android.xml"
        XMLReader xmlReader = saxParser.getXMLReader();
        xmlReader.setContentHandler(new XmlHandler(context, vector));
        xmlReader.parse(new InputSource(resource));
    }

    private static void parseVectorElement(Vector vector, Attributes attributes, Context context){
        vector.inflate(context, attributes);
        LogUtil.i("vector: %s", JsonUtil.toJson(vector));
    }

    private static void parseVectorElement(Vector vector, List<Attribute> attributes, Context context){
        vector.inflate(context, attributes);
        LogUtil.i("vector: %s", JsonUtil.toJson(vector));
    }

    private static Group parseGroupElement(Context context, Attributes attributes){
        return new Group(context, attributes);
    }

    private static Group parseGroupElement(Context context, List<Attribute> attributes){
        return new Group(context, attributes);
    }

    private static RichPath parsePathElement(Context context, Attributes attributes){
        String pathData = getAttributeString(attributes, "pathData", null);
        if (pathData == null)return null;
        LogUtil.i("path data: %s", pathData);
        RichPath path = new RichPath(context.getUITaskDispatcher(), pathData);
        path.inflate(context, attributes);
        return path;
    }

    private static RichPath parsePathElement(Context context, List<Attribute> attributes){
        String pathData = getAttributeString(attributes, "pathData", null);
        if (pathData == null)return null;
        LogUtil.i("path data: %s", pathData);
        RichPath path = new RichPath(context.getUITaskDispatcher(), pathData);
        path.inflate(context, attributes);
        return path;
    }

    public static String getAttributeString(Attributes attributes, String attributeName, String defaultValue){
        String value = getAttributeValue(attributes, attributeName);
        return TextTool.isNullOrEmpty(value) ? defaultValue : value;
    }

    public static String getAttributeString(List<Attribute> attributes, String attributeName, String defaultValue){
        if (attributes==null||attributes.size()<=0||TextTool.isNullOrEmpty(attributeName)){
            return defaultValue;
        }
        for (int i=0; i<attributes.size(); i++){
            Attribute attribute = attributes.get(i);
            if (attributeName.equalsIgnoreCase(attribute.getName())){
                return attribute.getStringValue();
            }
        }
        return defaultValue;
    }

    public static float getAttributeFloat(Attributes attributes, String attributeName, float defaultValue){
        String value = getAttributeValue(attributes, attributeName);
        return TextTool.isNullOrEmpty(value) ? defaultValue : Float.parseFloat(value);
    }

    public static float getAttributeFloat(List<Attribute> attributes, String attributeName, float defaultValue){
        String value = getAttributeString(attributes, attributeName, "");
        return TextTool.isNullOrEmpty(value) ? defaultValue : Float.parseFloat(value);
    }

    public static float getAttributeDimension(Context context, Attributes attributes, String attributeName, float defaultValue){
        String value = getAttributeValue(attributes, attributeName);
        return TextTool.isNullOrEmpty(value) ? defaultValue : Utils.getDimensionFromString(value);
    }

    public static float getAttributeDimension(List<Attribute> attributes, String attributeName, float defaultValue){
        String value = getAttributeString(attributes, attributeName, "");
        return TextTool.isNullOrEmpty(value) ? defaultValue : Utils.getDimensionFromString(value);
    }

    public static boolean getAttributeBoolean(Attributes attributes, String attributeName, boolean defaultValue){
        String value = getAttributeValue(attributes, attributeName);
        return TextTool.isNullOrEmpty(value) ? defaultValue : Boolean.parseBoolean(value);
    }

    public static boolean getAttributeBoolean(List<Attribute> attributes, String attributeName, boolean defaultValue){
        String value = getAttributeString(attributes, attributeName, "");
        return TextTool.isNullOrEmpty(value) ? defaultValue : Boolean.parseBoolean(value);
    }

    public static int getAttributeInt(Attributes attributes, String attributeName, int defaultValue){
        String value = getAttributeValue(attributes, attributeName);
        return TextTool.isNullOrEmpty(value) ? defaultValue : Integer.parseInt(value);
    }

    public static int getAttributeColor(Attributes attributes, String attributeName, int defaultValue){
        String value = getAttributeValue(attributes, attributeName);
        int colorValue = TextTool.isNullOrEmpty(value) ? defaultValue : Utils.getColorFromString(value);
        return colorValue==0 ? defaultValue : colorValue;
    }

    public static int getAttributeColor(List<Attribute> attributes, String attributeName, int defaultValue){
        String value = getAttributeString(attributes, attributeName, "");
        int colorValue = TextTool.isNullOrEmpty(value) ? defaultValue : Utils.getColorFromString(value);
        return colorValue==0 ? defaultValue : colorValue;
    }

    public static Paint.StrokeCap getAttributeStrokeLineCap(Attributes attributes, String attributeName, Paint.StrokeCap defaultValue){
        return getStrokeLineCap(getAttributeString(attributes, attributeName, ""), defaultValue);
    }

    public static Paint.StrokeCap getAttributeStrokeLineCap(List<Attribute> attributes, String attributeName, Paint.StrokeCap defaultValue){
        return getStrokeLineCap(getAttributeString(attributes, attributeName, ""), defaultValue);
    }

    public static Paint.Join getAttributeStrokeLineJoin(Attributes attributes, String attributeName, Paint.Join defaultValue){
        return getStrokeLineJoin(getAttributeString(attributes, attributeName, ""), defaultValue);
    }

    public static Paint.Join getAttributeStrokeLineJoin(List<Attribute> attributes, String attributeName, Paint.Join defaultValue){
        return getStrokeLineJoin(getAttributeString(attributes, attributeName, ""), defaultValue);
    }

    public static Path.FillType getAttributePathFillType(Attributes attributes, String attributeName, Path.FillType defaultValue){
        return getPathFillType(getAttributeString(attributes, attributeName, ""), defaultValue);
    }

    public static Path.FillType getAttributePathFillType(List<Attribute> attributes, String attributeName, Path.FillType defaultValue){
        return getPathFillType(getAttributeString(attributes, attributeName, ""), defaultValue);
    }

    private static String getAttributeValue(Attributes attributes, String attributeName) {
        return attributes.getValue(NAMESPACE, attributeName);
    }

    private static Paint.StrokeCap getStrokeLineCap(String cap, Paint.StrokeCap defValue){
        switch (cap){
            case "butt":
                return Paint.StrokeCap.BUTT_CAP;
            case "round":
                return Paint.StrokeCap.ROUND_CAP;
            case "square":
                return Paint.StrokeCap.SQUARE_CAP;
            default:
                return defValue;
        }
    }

    private static Paint.Join getStrokeLineJoin(String join, Paint.Join defValue){
        switch (join){
            case "miter":
                return Paint.Join.MITER_JOIN;
            case "round":
                return Paint.Join.ROUND_JOIN;
            case "bevel":
                return Paint.Join.BEVEL_JOIN;
            default:
                return defValue;
        }
    }

    private static Path.FillType getPathFillType(String fillType, Path.FillType defValue){
        switch (fillType){
            case "nonZero":
                return Path.FillType.WINDING_ORDER;
            case "evenOdd":
                return Path.FillType.EVEN_ODD;
//            case "":
//                return Path.FillType.INVERSE_WINDING;
//            case "":
//                return Path.FillType.INVERSE_EVEN_ODD;
            default:
                return defValue;
        }
    }

    private static void parseXML(Context context, Vector vector, SolidXml solidXml){
        Stack<Node> nodes = new Stack<>();
        Node node = solidXml.getRoot();
        Stack<Group> groupStack = new Stack<>();
        while(node != null){
            LogUtil.i("name: %s attr: %s", node.getName(), JsonUtil.toJson(node.getAttributes()));
            switch (node.getName()) {
                case Vector.TAG_NAME:
                    nodes.push(node);

                    parseVectorElement(vector, node.getAttributes(), context);

                    node = node.getChild();
                    break;
                case Group.TAG_NAME:
                    nodes.push(node);
                    Group group = parseGroupElement(context, node.getAttributes());
                    if (!groupStack.empty()) {
                        group.scale(groupStack.peek().matrix());
                    }
                    groupStack.push(group);
//                    LogUtil.i("group: %s", JsonUtil.toJson(group));
                    node = node.getChild();
                    break;
                case RichPath.TAG_NAME:
                    nodes.push(node);

                    RichPath path = parsePathElement(context, node.getAttributes());
                    if (!groupStack.empty()) {
                        path.applyGroup(groupStack.peek());
                    }
//                    LogUtil.i("path: %s", JsonUtil.toJson(path));
                    vector.getPaths().add(path);

                    node = node.getSibling();
                    if (node==null){
                        if(!groupStack.empty())groupStack.pop();
                        nodes.pop();
                    }
                    break;
            }
            while(node==null&&!nodes.isEmpty()){
                node=nodes.pop().getSibling();
            }
        }


    }

    public static class XmlHandler extends DefaultHandler{

        private Context context;
        private Vector vector;
        private Stack<Group> groupStack = new Stack<>();

        public XmlHandler(Context context, Vector vector) {
            this.context = context;
            this.vector = vector;
        }

        @Override
        public void startDocument() throws SAXException {
            super.startDocument();
        }

        @Override
        public void endDocument() throws SAXException {
            super.endDocument();
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            super.startElement(uri, localName, qName, attributes);
            LogUtil.i("qName: %s, attr: %s", qName, JsonUtil.toJson(attributes));
            switch (qName) {
                case Vector.TAG_NAME:
                    parseVectorElement(vector, attributes, context);
                    break;
                case Group.TAG_NAME:
                    Group group = parseGroupElement(context, attributes);
                    if (!groupStack.empty()) {
                        group.scale(groupStack.peek().matrix());
                    }
                    groupStack.push(group);
                    LogUtil.i("group: %s", JsonUtil.toJson(group));
                    break;
                case RichPath.TAG_NAME:
                    RichPath path = parsePathElement(context, attributes);
                    if (!groupStack.empty()) {
                        path.applyGroup(groupStack.pop());
                    }
//                    LogUtil.i("path: %s", JsonUtil.toJson(path));
                    vector.getPaths().add(path);
                    break;
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            super.endElement(uri, localName, qName);
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            super.characters(ch, start, length);
        }
    }
}
